package com.wiscamp.ninechapters.problems.infrastructure.repositories;

import com.wiscamp.ninechapters.problems.domain.gateway.TagGateway;
import com.wiscamp.ninechapters.problems.domain.models.problems.problems.*;
import com.wiscamp.ninechapters.problems.domain.models.problems.questions.Question;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemRepository;
import com.wiscamp.ninechapters.problems.enums.Actions;
import com.wiscamp.ninechapters.problems.infrastructure.converters.ProblemConverter;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.problems.ProblemMapper;
import com.wiscamp.ninechapters.problems.infrastructure.mappers.problems.ProblemViewMapper;
import com.wiscamp.ninechapters.problems.infrastructure.po.problems.ProblemView;
import com.wiscamp.ninechapters.problems.infrastructure.po.problems.QuestionPO;
import cube.ddd.aspect.HandleDataException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.domain.BaseRepository;
import cube.ddd.utils.PersistenceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class ProblemRepositoryImpl extends BaseRepository implements ProblemRepository {

    private final ProblemMapper problemMapper;

    private final ProblemViewMapper problemViewMapper;

    private final ProblemAccessor problemAccessor;

    private final ProblemBuilder problemBuilder;

    private final TagGateway tagGateway;

    public ProblemRepositoryImpl(ProblemMapper problemMapper, ProblemViewMapper problemViewMapper, ProblemAccessor problemAccessor, ProblemBuilder problemBuilder, TagGateway tagGateway) {
        this.problemMapper = problemMapper;
        this.problemViewMapper = problemViewMapper;
        this.problemAccessor = problemAccessor;
        this.problemBuilder = problemBuilder;
        this.tagGateway = tagGateway;
    }


    //region problem
    @HandleDataException
    public Problem getProblem(long problemId) {
        return problemBuilder.getProblem(problemId);
    }

    @HandleDataException
    public PagedObjects<Problem> getProblemsByQuery(@NotNull PagedQuery query) {
        PagedObjects<ProblemView> pagedObjects = PersistenceUtils.getPersistenceObjectsByQuery(query, problemViewMapper::findAll);
        return PersistenceUtils.convertPagedObjects(pagedObjects, ProblemConverter.Instance::toProblemByView);
    }

    @HandleDataException
    public Collection<Problem> getProblemsByIds(Collection<Long> problemIds) {
        return problemBuilder.getProblemsByIds(problemIds);
    }

    /*@HandleDataException
    public Collection<Problem> getSortedProblemsByDifficulty(@NotBlank Collection<Long> problemIds) {
        var sortedProblemIds = getSortedProblemIdsByDifficulty(problemIds);
        var sortedProblems = new ArrayList<Problem>();
        for (var problemId : sortedProblemIds) {
            var problem = getProblem(problemId);
            sortedProblems.add(problem);
        }
        return sortedProblems;
    }*/

    /*
    @HandleDataException
    public Collection<ItemPO> getClassifiedProblems(@NotBlank Collection<Long> tagIds, @NotBlank Collection<Long> pointIds,
                                                    @NotBlank Collection<Long> cognitiveDomainTagIds, @Min(0) int startDifficulty, @Min(0) int endDifficulty, @Min(0) int itemQuantity) {
        // 根据难度值获取标注动态测评标签的题目编号列表
        HashSet<Long> testingItems = getProblemIdsByTagAndDifficulty(tagIds, startDifficulty, endDifficulty);
        // 根据难度值获取认知层次匹配题目编号列表
        HashSet<Long> cognitiveDomainItems = getProblemIdsByTagAndDifficulty(cognitiveDomainTagIds, startDifficulty,
                endDifficulty);
        // 根据难度值标注知识点的题目编号列表
        HashSet<Long> pointItems = getProblemIdsByPointIdsAndDifficulty(pointIds, startDifficulty,
                endDifficulty);

        logger.debug("满足动态测评标签的题数：{}/{}", tagIds, testingItems.size());
        logger.debug("满足认知级别标签的题数：{}/{}", cognitiveDomainTagIds, cognitiveDomainItems.size());
        logger.debug("满足知识点标注的题数：{}/{}", pointIds, pointItems.size());

        testingItems.retainAll(pointItems);
        logger.debug("满足动态测评标签及知识点交集的题数：{}", testingItems.size());
        cognitiveDomainItems.retainAll(pointItems);
        logger.debug("满足认知级别标签及知识点交集的题数：{}", cognitiveDomainItems.size());
        testingItems.retainAll(cognitiveDomainItems);
        logger.debug("满足条件总题数 / 需要的题数：{}/{}", testingItems.size(), itemQuantity);

        Collection<Long> itemIds = RandomHelper.getRandomCollection(testingItems, itemQuantity);
        return problemAccessor.getItemsByIds(itemIds);
    }*/

    // endregion

    // region get problem ids
    private Collection<Long> getProblemIdsByPointIdsAndDifficulty(@NotEmpty Collection<Long> pointIds, @Min(0) int startDifficulty,
                                                                  @Min(0) int endDifficulty) {
        // Gets sub points by parent point.
        Collection<Long> subPointIds = tagGateway.getChildKnowledgePointIds(pointIds);
        return getProblemIdsByPointAndDifficulty(subPointIds, startDifficulty, endDifficulty);
    }


    /**
     * Sorts id collection of items by difficulty
     *
     * @param problemIds
     * @return
     */
    @HandleDataException
    public Collection<Long> getSortedProblemIdsByDifficulty(@NotEmpty Collection<Long> problemIds) {
        return problemViewMapper.findProblemIdsByDifficultyAsc(problemIds);
    }

    /**
     * Gets id collection of items by point and difficulty
     *
     * @param pointIds
     * @param startDifficulty
     * @param endDifficult
     * @return
     */
    @HandleDataException
    public Collection<Long> getProblemIdsByPointAndDifficulty(@NotEmpty Collection<Long> pointIds, @Min(0) int startDifficulty,
                                                              @Min(0) int endDifficulty) {
        var problemIds = tagGateway.getProblemIdsByKnowledgePoints(pointIds);
        return problemViewMapper.findProblemIdsByDifficulty(problemIds, startDifficulty, endDifficulty);
    }

    /**
     * Gets id collection of items by tag and difficulty
     *
     * @param tagIds
     * @param startDifficulty
     * @param endDifficulty
     * @return
     */
    @HandleDataException
    public Collection<Long> getProblemIdsByTagAndDifficulty(@NotEmpty Collection<Long> tagIds, @Min(0) int startDifficulty,
                                                            @Min(0) int endDifficulty) {
        var problemIds = tagGateway.getProblemIdsByTags(tagIds);
        return problemViewMapper.findProblemIdsByDifficulty(problemIds, startDifficulty, endDifficulty);
    }

    // endregion



    // region problem
    public boolean updateProblem(@NotNull Problem problem) {
        var problemPO = problemAccessor.toProblemPO(problem);
        problemAccessor.updateProblemPO(problemPO);
        return true;
    }
    // endregion

    // region simple problem
    public SimpleProblem createSimpleProblem(@NotNull SimpleProblem problem) {
        var problemPO = ProblemConverter.Instance.toProblemPO(problem);
        problemPO.setDifficulty(problem.getCurrentDifficulty());
        problemPO.setProblemScore(problem.getTotalScore());
        var newProblemPO = problemAccessor.createProblemPO(problemPO);
        long newProblemId = newProblemPO.getProblemId();

        // create collectionProblem
        problemAccessor.addCollectionProblem(problem.getCollectionId(), 0, 0, newProblemId, 0);

        // create question
        var firstQuestion = problem.getCurrentQuestion();
        firstQuestion.setProblemId(newProblemId);
        createQuestion(firstQuestion);
        return (SimpleProblem) getProblem(newProblemId);
    }

    public void updateSimpleProblem(@NotNull SimpleProblem problem) {
        var problemPO = ProblemConverter.Instance.toProblemPO(problem);
        var firstQuestion = problem.getCurrentQuestion();
        // set problem score and difficulty
        problemPO.setDifficulty(firstQuestion.getDifficulty());
        problemPO.setProblemScore(firstQuestion.getQuestionScore());

        var updatedProblemPO = problemAccessor.updateProblemPO(problemPO);
        long newProblemId = updatedProblemPO.getProblemId();

        // 简单题问题仅允许修改
        if (firstQuestion.getAction() == Actions.UPDATE) {
            firstQuestion.setProblemId(newProblemId);
            updateQuestion(firstQuestion);
        }
        //return null;
    }
    // endregion

    // region composite problem
    public Problem createCompositeProblem(CompositeProblem compositeProblem) {
        var problemPO = problemAccessor.toProblemPO(compositeProblem);
        problemPO.setDifficulty(compositeProblem.getCurrentDifficulty());
        problemPO.setProblemScore(compositeProblem.getTotalScore());
        var newProblemPO = problemMapper.saveAndFlush(problemPO);
        long newProblemId = newProblemPO.getProblemId();

        // create collectionProblem
        problemAccessor.addCollectionProblem(compositeProblem.getCollectionId(), 0, 0, newProblemId, 0);

        // create stem
        var stem = compositeProblem.getStem();
        if (Objects.nonNull(stem)) {
            stem.setProblemId(newProblemId);
            createStem(stem);
        }

        var questions = compositeProblem.getCurrentQuestions();
        for (var question : questions) {
            question.setProblemId(newProblemId);
            createQuestion(question);
        }
        return getProblem(newProblemId);
    }

    public void updateCompositeProblem(CompositeProblem compositeProblem) {
        var problemPO = problemAccessor.toProblemPO(compositeProblem);
        problemPO.setDifficulty(compositeProblem.getCurrentDifficulty());
        problemPO.setProblemScore(compositeProblem.getTotalScore());
        var newProblemPO = problemMapper.saveAndFlush(problemPO);
        long newProblemId = newProblemPO.getProblemId();

        // update stem
        var stem = compositeProblem.getStem();
        if (Objects.nonNull(stem)) {
            updateStem(stem);
        }
        // update questions
        var questions = compositeProblem.getCurrentQuestions();
        for (var question : questions) {
            question.setProblemId(newProblemId);
            updateQuestion(question);
        }
    }
    // endregion

    // region hybrid problem
    public Problem createHybridProblem(@NotNull HybridProblem hybridProblem) {
        var problemPO = problemAccessor.toProblemPO(hybridProblem);
        problemPO.setDifficulty(hybridProblem.getCurrentDifficulty());
        problemPO.setProblemScore(hybridProblem.getTotalScore());
        var newProblemPO = problemMapper.saveAndFlush(problemPO);
        long newProblemId = newProblemPO.getProblemId();

        // create collectionProblem
        problemAccessor.addCollectionProblem(hybridProblem.getCollectionId(), 0, 0, newProblemId, 0);

        // create stem
        var stem = hybridProblem.getStem();
        if (Objects.nonNull(stem)) {
            stem.setProblemId(newProblemId);
            createStem(stem);
        }

        // create questions
        var questions = hybridProblem.getCurrentQuestions();
        for (var question : questions) {
            question.setProblemId(newProblemId);
            createQuestion(question);
        }
        return getProblem(newProblemId);
    }

    public void updateHybridProblem(HybridProblem hybridProblem) {
        var problemPO = problemAccessor.toProblemPO(hybridProblem);
        problemPO.setDifficulty(hybridProblem.getCurrentDifficulty());
        problemPO.setProblemScore(hybridProblem.getTotalScore());
        var newProblemPO = problemMapper.saveAndFlush(problemPO);
        long newProblemId = newProblemPO.getProblemId();

        // update stem
        var stem = hybridProblem.getStem();
        if (Objects.nonNull(stem)) {
            stem.setProblemId(newProblemId);
            updateStem(stem);
        }

        // update questions
        var questions = hybridProblem.getCurrentQuestions();
        for (var question : questions) {
            question.setProblemId(newProblemId);
            updateQuestion(question);
        }
    }
    // endregion

    // region stem
    public void createStem(@NotNull Stem stem) {
        var stemPO = problemAccessor.toStemPO(stem);
        problemAccessor.createStemPO(stemPO);
    }

    public void updateStem(@NotNull Stem stem) {
        var stemPO = problemAccessor.toStemPO(stem);
        problemAccessor.updateStemPO(stemPO);
    }
    // endregion

    // region question
    private QuestionPO createQuestion(Question question) {
        var questionPO = problemAccessor.toQuestionPO(question);
        return problemAccessor.createQuestionPO(questionPO);
    }

    private void updateQuestion(@NotNull Question question) {
        var questionPO = problemAccessor.toQuestionPO(question);
        problemAccessor.updateQuestionPO(questionPO);
    }
    // endregion

}
